import json
import random
import re
from collections import defaultdict
from datetime import timedelta, datetime
from json import JSONDecodeError

import aiohttp
import time
import sys
import aioredis
import lxml.html
import pymongo
from tornado import gen
import asyncio
from faker import Faker
from util.qxnw import get_weixin_h5, cmcc_query
INTEVAL = 0.2
QUERY_TIME = 60 * 6
lock = asyncio.Lock()

from baseHandler import init_logger, PayHandler, Application

RECHANGE_QUEUE = defaultdict(asyncio.Queue)

class UniWxHandler(PayHandler):
    @gen.coroutine
    def post(self):
        db = self.application.db
        exited = yield self.merchant_exit()
        if not exited:
            logger.info("商户不存在")
            return self.write(json.dumps({"message": "商户不存在", "ret": -1}))
        if not self.check_ip():
            return self.write(json.dumps({"message": "ip错误", "ret": -1}))
        if not self.check_sign():
            logger.info("签名验证失败")
            return self.write(json.dumps({"message": "签名验证失败", "ret": -1}))
        data = self.decrypt()
        if not data:
            return self.write({"message": "解密失败", "ret": -1})
        data = json.loads(data)
        logger.info(data)
        arguments = ["payorderno", "amount"]
        for arg in arguments:
            if arg not in data:
                return self.write({"ret": -1, "message": "缺少%s" % arg})

        # is_close = yield self.application.redis.get("is_unicom_close", encoding='utf-8')
        # if is_close == '1':
        #     logger.info("%s|通道关闭" % data["payorderno"])
        #     return self.write({"ret": -1, "message": "通道关闭"})

        obj = yield db.rechange.find_one({"payorderno": data["payorderno"]})

        if obj:
            logger.info(obj)
            return self.write({"message": "订单号重复", "ret": -1})
        logger.info("支付订单%s|%s|%s", self.merchantid, data["payorderno"], data['amount'])
        supplier = self.merchant_info.get("supplier")
        if not supplier:
            return self.write({"message": "无有效供应商", "ret": -1})

        while True:
            try:
                if data["amount"] in ('20', '30', '50', '100', '200', '300', '500'):
                    queue_name = "UN_%s_%s" % (supplier, data["amount"])
                    logger.info(queue_name)
                    obj = RECHANGE_QUEUE[queue_name].get_nowait()
                else:
                    return self.write({"message": "不支持的金额", "ret": -1})
                # logger.info(obj)
                if not obj:
                    return self.write({'ret': -1, 'message': "无有效订单"})
                if time.time() - obj["time"] > 58:
                    if time.time() - obj['time'] < 60:
                        yield db.rechange.update_one({"ordernumber": obj["ordernumber"]},
                                                     {"$set": {
                                                         "orderstate": "1"
                                                     }})
                    continue
                yield db.rechange.update_one({"ordernumber": obj["ordernumber"]},
                                             {"$set": {
                                                 "orderstate": "8"
                                             }})

                try:
                    future = get_weixin_h5(obj['tel_number'], obj['amount'], self.application.redis)
                    result = yield gen.with_timeout(timedelta(seconds=30), future)

                except aiohttp.ClientConnectorError as e:
                    logger.info("%s|代理失效", obj["tel_number"])
                    result = {'code': -1, 'message': "proxy error"}
                except JSONDecodeError as e:
                    logger.error(e, exc_info=True)
                    result = {'code': -1, 'message': "proxy error"}
                except Exception as e:
                    logger.info("%s|取单失败|%s", obj["tel_number"], logger.info(type(e).__name__))
                    logger.error(e, exc_info=True)
                    result = {'code': -2, 'message': "error"}

                url = ""
                if result['code'] == 0:
                    wx_url = result['url']
                    orderid = result['orderid']
                    url = 'http://hh.91link.link:8091/charge/pay.php?orderNubmer=%s' % obj['ordernumber']
                    orderstate = '8'

                else:
                    orderstate = '1'
                    wx_url = None
                    orderid = None

                res = {"orderstate": orderstate, "message": result.get('message', ''), 'wx_url': url,
                       "orderid": orderid}

                if orderstate == '8':
                    yield db.rechange.update_one({"ordernumber": obj["ordernumber"]},
                                                 {"$set": {
                                                     "payorderno": data["payorderno"],
                                                     "merchantuid": self.merchantid,
                                                     "orderstate": orderstate,
                                                     "orderid": orderid,
                                                     "wx_url": wx_url,
                                                     "pay_backurl": data.get("backurl"),
                                                     "message": result.get('message', ''),
                                                     "prov": result.get("prov"),
                                                     "tel_type": result.get("tel_type")
                                                 }
                                                 })
                if orderstate == '1':
                    yield db.rechange.update_one({"ordernumber": obj["ordernumber"]},
                                                 {"$set": {
                                                     "orderstate": orderstate,
                                                     "message": result.get('message', '')
                                                 }
                                                 })

                return_obj = self.niti_data(res, {"MERCHANTID": self.merchantid})
                return_obj.update({"ret": 0, "message": "success"})
                self.write(return_obj)
                self.finish()
                return
            except asyncio.QueueEmpty:
                return self.write({'ret': -1, 'message': "无有效订单"})


class UnRechargeHandler(PayHandler):
    @gen.coroutine
    def post(self):
        logger.info(self.request.path)
        db = self.application.db
        exited = yield self.merchant_exit()
        if not exited:
            logger.info("商户不存在")
            return self.write(json.dumps({"message": "商户不存在", "ret": -1}))
        if not self.check_ip():
            return self.write(json.dumps({"message": "ip错误", "ret": -1}))
        if not self.check_sign():
            logger.info("签名验证失败")
            return self.write(json.dumps({"message": "签名验证失败", "ret": -1}))
        data = self.decrypt()
        if not data:
            return self.write({"message": "解密失败", "ret": -1})
        data = json.loads(data)
        logger.info(data)
        arguments = ["ordernumber", "amount", "tel_number"]
        for arg in arguments:
            if arg not in data:
                return self.write({"ret": -1, "message": "缺少%s" % arg})
        logger.info("充值%s|%s|%s", data['ordernumber'], data['tel_number'], data['amount'])

        is_risk = yield self.application.redis.sismember("risk_number", data["tel_number"])
        if is_risk:
            logger.info("不到账%s" % data["tel_number"])
            return self.write({"ret": -1, "message": "风险号码"})
        res = self.check_ordernumber(data)
        if res:
            return self.write(res)
        start_time = time.time()

        obj = {
            "tel_number": data["tel_number"],
            "ordernumber": data["ordernumber"],
            "time": start_time,
            "amount": data["amount"],
            # "tel_type": "unicom"
        }

        data['time'] = start_time
        data["orderdate"] = datetime.now().strftime('%Y%m%d')
        data["merchantid"] = self.merchantid
        data['tel_type'] = 'unicom'
        data['orderstate'] = '2'
        try:
            yield db.rechange.insert_one(data)
            self.application.redis.lpush('unicom_recharge', json.dumps({"ordernumber": data['ordernumber']}))
        except pymongo.errors.DuplicateKeyError:
            return self.write({"ret": -1, "message": "重复的订单号"})

        if data["amount"] in ('20', '30', '50', '100', '300'):
            queue_name = "UN_%s_%s" % (self.merchantid, data["amount"])
            yield from RECHANGE_QUEUE[queue_name].put(obj)
            logger.info(RECHANGE_QUEUE[queue_name].qsize())
        else:
            return self.write({"message": "不支持的金额", "ret": -1})

        res = {"orderstate": '2', "message": "订单提交成功", }
        return_obj = self.niti_data(res, {"MERCHANTID": self.merchantid})
        return_obj.update({"ret": 0, "message": "success"})
        self.write(return_obj)
        self.finish()


async def query(query_obj, db, redis):
    now = time.time()
    obj = await db.rechange.find_one(query_obj)
    extra = {}
    if not obj:
        orderstate = "1"
        message = "订单号不存在"
        return orderstate, message, extra

    orderstate = obj.get("orderstate", "2")

    if orderstate in ("0", "1", "5", "6"):
        message = obj.get("message", "")
        extra = {
            "tel_number": obj["tel_number"],
            "orderid": obj.get("orderid", ""),
        }
    else:
        async with lock:
            obj = await db.rechange.find_one(query_obj)
            query_time = obj.get("query_time") or obj["time"]
            if time.time() - query_time < 15:
                message = ""
                return orderstate, message, extra

            query_time = time.time()
            await db.rechange.update_one(query_obj, {"$set": {"query_time": query_time}})

        if obj.get('wx_url'):
            # if obj.get("tel_type", "") == 'unicom':
            orderstate, message = await cmcc_query(obj['orderid'], "", redis)
            # else:
            #     orderstate = '9'
            #     message = '订单类型错误'
            if orderstate == '8':
                if query_time - obj['time'] > QUERY_TIME:
                    orderstate = '1'
                else:
                    orderstate = obj.get("orderstate")
            extra = {
                "tel_number": obj["tel_number"],
                "orderid": obj.get("orderid", ""),
                # "url": obj.get("url", "")
            }
        else:
            if time.time() - obj['time'] < 60 and obj.get('orderstate', '2') == '2':
                orderstate = '2'
                message = obj.get("message", "取单中")
            elif time.time() - obj['time'] < 92 and obj.get('orderstate', '2') == '8':
                orderstate = '8'
                message = '获取二维码中'
            else:
                orderstate = '1'
                message = '未支付2'
            extra = {
                "tel_number": obj["tel_number"],
                "orderid": obj.get("orderid", ""),
                # "url": obj.get("url", "")
            }
        await db.rechange.update_one(query_obj, {
            "$set": {"orderstate": orderstate, "query_time": query_time, "message": message}})
    return orderstate, message, extra


class UnicomQuery(PayHandler):
    @gen.coroutine
    def post(self):
        db = self.application.db
        exited = yield self.merchant_exit()
        if not exited:
            logger.info("商户不存在")
            return self.write(json.dumps({"message": "商户不存在", "ret": -1}))
        if not self.check_ip():
            return self.write(json.dumps({"message": "ip错误", "ret": -1}))
        if not self.check_sign():
            logger.info("签名验证失败")
            return self.write(json.dumps({"message": "签名验证失败", "ret": -1}))
        data = self.decrypt()
        if not data:
            return self.write({"message": "解密失败", "ret": -1})
        data = json.loads(data)

        if data.get("ordernumber"):
            query_obj = {"ordernumber": data["ordernumber"]}
        elif data.get("payorderno"):
            query_obj = {"payorderno": data["payorderno"]}

        else:
            return self.write({"message": "无效参数", "ret": -1})
        orderstate, message, extra = yield query(query_obj, db, self.application.redis)
        logger.info("查询%s|%s|%s|%s", data.get("ordernumber"), data.get("payorderno"), orderstate, message)
        res = {"orderstate": orderstate, "message": message, **extra}
        return_obj = self.niti_data(res, {"MERCHANTID": self.merchantid})
        return_obj.update({"ret": 0, "message": "success"})
        self.write(return_obj)


if __name__ == "__main__":
    handlers = [
        (r"/pay/un_recharge", UnRechargeHandler),
        (r"/pay/un_wxqrcode", UniWxHandler),
        (r"/pay/un_query", UnicomQuery),
        # (r"/pay/queue", QueueHandler),
        # (r"/set_querytime/(\d+)", SetTimeHandler)
    ]
    QUERY_TIME = 60 * 6
    application = Application(handlers, debug=False)
    port = sys.argv[1]
    application.listen(port, address="127.0.0.1")
    logger = init_logger("cmcc", "/app/log/cs_unicom/unicom.log")
    logger.info("Run unionpay server ... http://127.0.0.1:%s", port)
    loop = asyncio.get_event_loop()
    application.redis = loop.run_until_complete(
        aioredis.create_redis(address=('localhost', 6379), db=0, loop=loop)
    )
    application.redis_unicom = loop.run_until_complete(
        aioredis.create_redis(address=('localhost', 6379), db=1, loop=loop)
    )
    application.init_with_loop(loop)
    loop.run_forever()
